#include <iostream>

#include "eda.h"
#include "loNetList.hpp"
#include "loScriptMove.hpp"
#include "loScriptMoveNext.hpp"
#include "loScriptMoveRandom.hpp"
#include "sisInterface.h"

class loHC: public edaHC {
public:
  loHC(): edaHC() {
    sisInit();
  }
  loHC( edaMove *move, edaMoveGen *next, edaMoveSelect *select )
    : edaHC( move, next, select ) {}
  loHC( const loHC &hc)
    : edaHC( hc ) {}
  ~loHC() {
    sisEnd();
  }

  setClassID( _USERCLASSID_ + _CLSID_LO_HC_ );
};

class loSA: public edaSA {
public:
  loSA(): edaSA() {
    sisInit();
  }
  loSA( edaMove *move, edaMoveGen *gen, edaContinue *cont, 
	double temp, edaCoolingSchedule *coolschedule )
    :   edaSA( move, gen, cont, temp, coolschedule )
  {}
  loSA( const loSA &sa )
    : edaSA( sa ) {}
  ~loSA() {
    sisEnd();
  }

  setClassID( _USERCLASSID_ + _CLSID_LO_SA_ );
};

class loTS: public edaTS {
public:
  loTS(): edaTS() {
    sisInit();
  }
  loTS( edaMove *move, edaMoveGen *next, 
	edaTabuList *tabulist, edaAspirCrit *aspircrit,
	edaContinue *contcrit )
    : edaTS( move, next, tabulist, aspircrit, contcrit ) {}
  loTS( edaMove *move, edaTSMoveExpl *moveexpl, edaContinue *contcrit )
    : edaTS( move, moveexpl, contcrit ) {}
  loTS( const loTS &ts)
    : edaTS( ts ) {}
  ~loTS() {
    sisEnd();
  }

  setClassID( _USERCLASSID_ + _CLSID_LO_TS_ );
};

void printHelp();

int main(int argc, char **argv)
{
  char scriptDir[256];
  char *blifFile = NULL;
  int c;
  int index;

  sprintf(scriptDir, ".");
  while ((c = getopt(argc, argv, "s:h?")) != -1 )
  {
    switch (c)
    {
      case 's':
        strncpy(scriptDir, optarg, 256);
        break;

      case '?':
      case 'h':
      default:
        printHelp();
        return 1;
    }
  }

  if (optind >= argc)
  {
    // There is no BLIF file
    printHelp();
    return 1;
  }

  blifFile = argv[optind];

  srand(time(NULL));

  // start SIS
  sisInit();

  loNetList netlist( blifFile );;

  loScriptMove script1, script2, script3, script4;
  if (scriptDir != NULL)
  {
    script1.setScriptDir(scriptDir);
    script2.setScriptDir(scriptDir);
    script3.setScriptDir(scriptDir);
    script4.setScriptDir(scriptDir);
  }

  loScriptMoveNext scriptNext1( 0, 5 );
  edaBestImprSelect moveSelect;
  loHC hcSearch( &script1,
		 &scriptNext1,
		 &moveSelect );

  loScriptMoveRandom script_random1( 0, 5 ), script_random2( 2, 7 );
  edaExpCoolingSchedule coolingSchedule1(0.1, 0.7), coolingSchedule2(0.5, 0.75);
  edaGenContinue cont_criteria1(6), cont_criteria2(7);
  loSA saSearch1( &script2,
		  &script_random1,
		  &cont_criteria1,
		  800.0,
		  &coolingSchedule1);
  loSA saSearch2( &script3,
		  &script_random2,
		  &cont_criteria2,
		  500.0,
		  &coolingSchedule2);

  loScriptMoveNext scriptNext2( 0, 5 );
  edaSimpleMoveTabuList tabuList;
  edaImprBestFitAspirCrit aspirCrit;
  edaGenContinue cont_criteria3(10);

  loTS tsSearch( &script4, &scriptNext2, &tabuList, &aspirCrit, &cont_criteria3);

  edaParWrapperControl sfControl;
  int hcid1 = sfControl.insertVertex( &hcSearch );
  int hcid2 = sfControl.insertVertex( &hcSearch );
  int said1 = sfControl.insertVertex( &saSearch1 );
  int said2 = sfControl.insertVertex( &saSearch2 );
  int tsid1 = sfControl.insertVertex( &tsSearch );
  sfControl.insertEdge( hcid1, said1 ); 
  sfControl.insertEdge( hcid1, said2 );
  sfControl.insertEdge( said1, hcid2 );
  sfControl.insertEdge( said2, hcid2 );
  sfControl.insertEdge( hcid1, tsid1 );
  sfControl.insertEdge( tsid1, hcid2 );

  std::cout << "[From Fitness] " << netlist.fullEvaluate() << std::endl;
  sfControl.search( netlist );
  std::cout << "[To Fitness] " << netlist.fullEvaluate() << std::endl;

  // end SIS
  sisEnd();

  return 0;
}

edaSerialize *userClassGenerate(int clsid)
{
  switch( clsid ){
  case _USERCLASSID_ + _CLSID_LO_NETLIST_:
    return new loNetList;
    
  case _USERCLASSID_ + _CLSID_SCRIPT_MOVE_:
    return new loScriptMove;
      
  case _USERCLASSID_ + _CLSID_SCRIPT_MOVE_RANDOM_:
    return new loScriptMoveRandom;
    
  case _USERCLASSID_ + _CLSID_SCRIPT_MOVE_NEXT_:
    return new loScriptMoveNext;
    
  case _USERCLASSID_ + _CLSID_LO_HC_:
    return new loHC;
    
  case _USERCLASSID_ + _CLSID_LO_SA_:
    return new loSA;
    
  case _USERCLASSID_ + _CLSID_LO_TS_:
    return new loTS;
    
  default:
    std::cerr << "Unknown classId " << clsid 
	      << " for object generation!!!" << std::endl;
    exit(-1);
  }
}

void printHelp()
{
  std::cerr << "Usage : ./loPFWSearch [-h] [-?] [-s <scripts dir>] <instance>" << std::endl;
  std::cerr << "  -h, -?             Print this help" << std::endl;
  std::cerr << "  -s <scripts dir>   Directory contains SIS scripts" << std::endl;
  std::cerr << "  <instance>         BLIF file to be optimize" << std::endl;
}
